package factories;

import java.util.Random;

import logic.nodes.damagable.DamagableNode;
import settings.Config;
import settings.GraphicSettings;

import com.jme.math.FastMath;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.scene.BillboardNode;
import com.jme.scene.Controller;
import com.jme.scene.Node;
import com.jme.scene.Spatial.CullHint;
import com.jme.scene.Spatial.LightCombineMode;
import com.jme.scene.shape.Cylinder;
import com.jme.scene.shape.Quad;
import com.jme.scene.state.BlendState;
import com.jme.scene.state.CullState;
import com.jme.scene.state.TextureState;
import com.jme.scene.state.ZBufferState;
import com.jme.system.DisplaySystem;
import com.jmex.effects.TrailMesh;
import com.jmex.effects.particles.ParticleFactory;
import com.jmex.effects.particles.ParticleMesh;

import fileHandling.CloneImportExport;
import fileHandling.TextureLoader;
import fileHandling.language.options.OptionValues;

public class EffectFactory {
		
	public static final int WEAPON_FIRE_SECTIONS = 4;
	public static final int NOZZLE_SECTIONS = 12;
	
	public static final int WEAPON_FIRE_TRAILS_LIMIT = 15;
	public static final int NOZZLE_TRAILS_LIMIT = 3;
	
	public static final float SIMPLE_EXPLOSION_SIZE = 5f;
	
	public static TextureState explosionTexture, blue, darkBlue, red, yellow, simpleExplosion;
	public static int weaponFireNumber, nodeNumber;
	public static ColorRGBA smokeColor = ColorRGBA.white;
	
	public static BlendState blendState, blackBlendState;
	public static ZBufferState zBufferState;
	public static CullState cullState;
	
	private static final Random r = new Random();
	
	private static boolean useSimpleExplosion;
	
	private static CloneImportExport savedWeaponFireCylinder;
		
	public static void init() {
		Cylinder c = new Cylinder("Cylinder", 4, 4, 0.3f, 7f, true);
		c.setLightCombineMode(LightCombineMode.Off);
		c.setLocalTranslation(new Vector3f(0f, 0f, c.getHeight()));
		savedWeaponFireCylinder = new CloneImportExport();
		savedWeaponFireCylinder.saveClone(c);
		
		updateGraphicSettings();
	}
	
	public static Cylinder getWeaponFireCylinder() {
		Cylinder c = (Cylinder)savedWeaponFireCylinder.loadClone();
		c.setRenderState(yellow);
		c.updateRenderState();
		return c;
	}
	
	public static TrailMesh getWeaponFireTrail(TextureState texture) {
		TrailMesh mesh = createNewTrail(WEAPON_FIRE_SECTIONS);
		mesh.setRenderState(texture);
		return mesh;
	}
	
	public static TrailMesh getNozzleTrail(TextureState texture) {
		TrailMesh mesh = createNewTrail(NOZZLE_SECTIONS);
		
		mesh.setRenderState(texture);
		return mesh;
	}
	
	private static TrailMesh createNewTrail(int sections) {		
		TrailMesh trail = new TrailMesh("WeaponFireTrail", sections);
		trail.setUpdateSpeed(80f);
		trail.setFacingMode(TrailMesh.FacingMode.Billboard);
		trail.setUpdateMode(TrailMesh.UpdateMode.Step);
		
		trail.setRenderQueueMode(Renderer.QUEUE_TRANSPARENT);
		trail.setCullHint(CullHint.Dynamic);
		trail.setCastsShadows(false);
		trail.setLightCombineMode(LightCombineMode.Off);
		
		trail.setRenderState(zBufferState);
		trail.setRenderState(blendState);
		trail.setRenderState(cullState);
		
		trail.updateRenderState();
		
		return trail;
	}
	
	public static void createWeaponExplosion(Node node, Vector3f location) {
		if(useSimpleExplosion && !Config.get().getDisableSimpleExplosions()) 
			createSimpleExplosion(node, location);
		else createSmallExplosion(weaponFireNumber, node, location);
	}
	
	public static void createNodeExplosion(DamagableNode node) {
		if(useSimpleExplosion && !Config.get().getDisableSimpleExplosions()) 
			createSimpleExplosion(node.getParent(), node.getLocalTranslation());
		else {
			createSmallExplosion(nodeNumber, node.getParent(), node.getLocalTranslation());
			createMediumExplosion(nodeNumber, node.getParent(), node.getLocalTranslation());
			createBigExplosion(nodeNumber, node.getParent(), node.getLocalTranslation());
		}
	}
	
	public static void createSecondaryNodeExplosion(Node model) {
		if(useSimpleExplosion && !Config.get().getDisableSimpleExplosions()) 
			createSimpleExplosion(model.getParent(), model.getLocalTranslation());
		else createMediumExplosion(EffectFactory.nodeNumber, model.getParent(), model.getLocalTranslation());
	}
	
	private static void createSmallExplosion(int number, Node parent, Vector3f location) {
		createExplosion(ColorRGBA.red, number, parent, location, 20f, 40f, 0.1f);
		createExplosion(ColorRGBA.orange, number, parent, location, 20f, 40f, 0.1f);
		createExplosion(ColorRGBA.yellow, number, parent, location, 20f, 40f, 0.1f);
		createExplosion(smokeColor, number, parent, location, 20f, 40f, 0.1f);
	}
	
	private static void createMediumExplosion(int number, Node parent, Vector3f location) {
		createExplosion(ColorRGBA.red, number, parent, location, 40f, 60f, 0.1f);
		createExplosion(ColorRGBA.orange, number, parent, location, 40f, 60f, 0.1f);
		createExplosion(ColorRGBA.yellow, number, parent, location, 40f, 60f, 0.1f);
		createExplosion(smokeColor, number, parent, location, 40f, 60f, 0.1f);
	}
	
	private static void createBigExplosion(int number, Node parent, Vector3f location) {
		createExplosion(ColorRGBA.red, number, parent, location, 60f, 80f, 0.1f);
		createExplosion(ColorRGBA.orange, number, parent, location, 60f, 80f, 0.1f);
		createExplosion(ColorRGBA.yellow, number, parent, location, 60f, 80f, 0.1f);
		createExplosion(smokeColor, number, parent, location, 40f, 60f, 0.1f);
	}
	
	public static void createExplosion(ColorRGBA initColor, int number, Node parent, 
			Vector3f location, float minLife, float maxLife, float initSpeed) {
		createExplosion(initColor, explosionTexture, number, parent, location, minLife, maxLife, initSpeed, 0.5f, 0.4f);
	}
	
	public static void createExplosion(ColorRGBA initColor, TextureState texture, int number, Node parent, 
			Vector3f location, float minLife, float maxLife, float initSpeed, float startSize, float endSize) {
		ParticleMesh explosion = ParticleFactory.buildParticles("explosion", number);
		explosion.setEmissionDirection(Vector3f.UNIT_Y.clone());
		
		explosion.setMaximumAngle(FastMath.PI);
		explosion.setMinimumAngle(0f);
		explosion.getParticleController().setSpeed(0.1f);
		explosion.setMinimumLifeTime(minLife);
		explosion.setMaximumLifeTime(maxLife);
		explosion.setStartSize(startSize);
		explosion.setEndSize(endSize);
		explosion.getParticleController().setControlFlow(false);
		explosion.getParticleController().setRepeatType(Controller.RT_CLAMP);
		explosion.setInitialVelocity(initSpeed);
		explosion.setStartColor(initColor);
		explosion.setEndColor(ColorRGBA.black);
		
		explosion.setRenderState(texture);
		explosion.setRenderState(zBufferState);
		
		if(initColor == smokeColor) explosion.setRenderState(blackBlendState);
		else explosion.setRenderState(blendState);
		explosion.updateRenderState();
		
		explosion.getLocalTranslation().set(location);
		parent.attachChild(explosion);
		explosion.updateGeometricState(0, true);
		explosion.forceRespawn();
		explosion.addController(new ParticleRemoveController(explosion));
	}
	
	private static void createSimpleExplosion(Node parent, Vector3f location) {
		BillboardNode bbNode = new BillboardNode("BillboardNode for simpleExplosion");
		bbNode.setLightCombineMode(LightCombineMode.Off);
		
		Quad[] quads = new Quad[3];
		for(int i = 0; i < 3; i++) {
			Quad quad = createExplosionQuad();
			quads[i] = quad;
			bbNode.attachChild(quad);
		}
		bbNode.addController(new SimpleExplosionController(bbNode, quads));
		
		bbNode.getLocalTranslation().set(location);
		bbNode.updateRenderState();
		
		parent.attachChild(bbNode);
	}
	
	private static Quad createExplosionQuad() {
		Quad quad = new Quad("explosion quad", SIMPLE_EXPLOSION_SIZE, SIMPLE_EXPLOSION_SIZE);
		quad.setRenderState(simpleExplosion);
		quad.setRenderState(zBufferState);
		quad.setRenderState(blendState);
		quad.getLocalRotation().fromAngles(0f, 0f, r.nextInt(3));
		quad.setRenderQueueMode(Renderer.QUEUE_TRANSPARENT);
		return quad;
	}
	
	public static void updateGraphicSettings() {
		Renderer renderer = DisplaySystem.getDisplaySystem().getRenderer();
		
		blendState = renderer.createBlendState();
		blendState.setBlendEnabled(true);
		blendState.setSourceFunction(BlendState.SourceFunction.SourceAlpha);
		blendState.setDestinationFunction(BlendState.DestinationFunction.One);
		blendState.setTestEnabled(true);
		
		blackBlendState = renderer.createBlendState();
		blackBlendState.setBlendEnabled(true);
		blackBlendState.setSourceFunction(BlendState.SourceFunction.SourceAlpha);
		blackBlendState.setDestinationFunction(BlendState.DestinationFunction.OneMinusSourceAlpha);
		blackBlendState.setTestEnabled(true);
		
		zBufferState = renderer.createZBufferState();
		zBufferState.setWritable(false);
		
		cullState = renderer.createCullState();
		cullState.setCullFace(CullState.Face.None);
		cullState.setEnabled(true);
		
		GraphicSettings settings = GraphicSettings.get();
		String qual = settings.getTextureQuality();
		
		String effectsPath = "data/textures/effects/";
		
		String simpleExplosionPath = effectsPath + "explosion/" + qual + ".png";
		simpleExplosion = TextureLoader.getTextureState(simpleExplosionPath);
		
		String bluePath = effectsPath + "particles/blueParticle/" + qual + ".png";
		blue = TextureLoader.getTextureState(bluePath);
		
		String darkBluePath = effectsPath + "particles/darkBlueParticle/" + qual + ".png";
		darkBlue = TextureLoader.getTextureState(darkBluePath);
		
		String yellowPath = effectsPath + "particles/yellowParticle/" + qual + ".png";
		yellow = TextureLoader.getTextureState(yellowPath);
		
		String explosionPath = effectsPath + "particles/roundParticle/" + qual + ".png";
		explosionTexture = TextureLoader.getTextureState(explosionPath);
		
		String effectsQual = settings.getEffectsQualityString();
		String highValue = OptionValues.High.toString();
		String medValue = OptionValues.Medium.toString();
		
		useSimpleExplosion = effectsQual.equals(OptionValues.Low.toString());
		
		if(effectsQual.equals(highValue)) {
			weaponFireNumber = 40;
			nodeNumber = 50;
		} else if(effectsQual.equals(medValue)) {
			weaponFireNumber = 30;
			nodeNumber = 40;
		} else {
			weaponFireNumber = 20;
			nodeNumber = 30;
		}
	}
}